package ocp.context;

import java.util.Iterator;
import java.util.Vector;
import java.util.Enumeration;
import java.util.Observer;
import ocp.context.adapter.*;

/**
 * A complex context is composed of one or more context, that can be simple
 * or complex each. The contextual information of a complex context is the
 * combination of all the contextual information from the subcontexts of this
 * context. This combination is obtained through an inteligent module that
 *
 * @author Ignacio Nieto Carvajal.
 */
public class ComplexContext extends Context implements java.io.Serializable {

    /** The subcontexts of this context */
    protected Vector contexts = null;
    /** The context merger that combines the subcontexts of this context */
    public ContextMerger merger = null;
    /** the observers of this complex context */
    public Vector observers = null;

    /**
     * This simplified constructor creates an empty complex context with a
     * dummy context merger. No contextual information merging will
     * be done besides from mere aggregation of contextual data.
     */
    public ComplexContext(String name, String id) {
        this(name, id, new SimpleContextMerger());
    }

    /** The main constructor of the class. Creates a empty complex context
     * with no subcontexts */
    public ComplexContext(String name, String id, ContextMerger cint) {
        super(name, id);
        contexts = new Vector();
        observers = new Vector();
        merger = cint;
    }

    /**
     * An alternate constructor of the class. It initialices this context with
     * an array of subcontexts.
     * @param subcontexts an array with contexts.
     */
    public ComplexContext(String name, String id, ContextMerger cint, Context[] subcontexts) {
        this(name, id, cint);
        for (int i = 0; i < subcontexts.length; i++) {
            contexts.addElement(subcontexts[i]);
        }
    }

    /**
     * This constrcutor takes a vector of contexts as an entry argument. Only
     * valid context are added to this context.
     */
    public ComplexContext(String name, String id, ContextMerger cint, Vector subcontexts) {
        this(name, id, cint);
        if (subcontexts != null) {
            Object o;
            for (int i = 0; i < subcontexts.size(); i++) {
                o = subcontexts.elementAt(i);
                if (o instanceof Context) {
                    contexts.addElement(o);
                }
            }
        }
    }

    /**
     * Adds a subcontext to this context.
     * @param c the context to add.
     */
    public void addContext(Context c) {
        contexts.addElement(c);
        for (int i = 0; i < observers.size(); i++) {
            Observer obs = (Observer) observers.elementAt(i);
            c.addObserver(obs);
        }
    }

    /**
     * A complex context observer must be aware of changes in every subcontext.
     */
    public synchronized void addObserver(Observer o) {
        super.addObserver(o);
        for (int i = 0; i < contexts.size(); i++) {
            Object subcontext = contexts.elementAt(i);
            if (subcontext instanceof Context) {
                ((Context) subcontext).addObserver(o);
            }
        }
    }

    /**
     * Removes a subcontext from this context.
     * @param c the context to remove.
     * @return true if context was found and removed, false otherwise.
     */
    public boolean removeContext(Context c) {
        return contexts.removeElement(c);
    }

    /**
     * Removes all subcontexts from this context, emptying the context.
     */
    public void clearContext() {
        contexts.removeAllElements();
    }

    public ContextualInformationList getContextualInformation() throws ContextualInformationAbsentException {
        if (contexts == null) {
            throw new ContextualInformationAbsentException("Empty contexts for this complex context.");
        }
        if (merger == null) {
            throw new ContextualInformationAbsentException("Contexts can not be proccessed. Bad context intelligence.");
        }
        return merger.mergeContexts(contexts);
    }

    /**
     * returns the list of contexts of this Complex Context
     */
    public Iterator getContexts() {
        return contexts.iterator();
    }

    /** updates the contextual information of this context, by updating
     * the information from all the subcontexts.
     * TODO: Quiz�s la información de contexto tenga relación de modo tal que
     * la actualizaci�n requiera de cierta inteligencia. Quiz�s haya que invocar
     * alg�n m�todo de ContextualInteligence.
     */
    public void updateContextualInformation() throws ContextualInformationAbsentException {
        if ((contexts == null) || (contexts.isEmpty())) {
            throw new ContextualInformationAbsentException("No context information from this context.");
        }
        for (int i = 0; i < contexts.size(); i++) {
            Context c = (Context) contexts.elementAt(i);
            try {
                c.updateContextualInformation();
            } catch (ContextualInformationAbsentException ciae) {
                System.err.println("Failed to get contextual information from" + c.getName() + ". Continuing...");
            }
        }
    }

    /**
     * This method returns a string containing a XML representation of the
     * complex context. This xml string is composed by a combination of
     * all the contextual information that the context has, interpreted
     * by the contextual merger.
     */
    public String toXML() {
        ContextualInformationList cil = merger.mergeContexts(contexts);
        String xml = "<" + this.getClass().getSimpleName() + " name=\"" + name + "\" id=\"" + id + "\" dateTime=\"" + getTime() +"\">\n";

        if (!contexts.isEmpty()) {
            xml += "\t<Contexts>\n";
            for (Enumeration e = contexts.elements(); e.hasMoreElements();) {
                xml += "\t" + ((Context) e.nextElement()).toXML() + "\n";
            }
            xml += "\t</Contexts>\n";
        }
        xml += "</" + this.getClass().getSimpleName() + ">\n";
        return xml;
    }

    public boolean loadFromXML(String xmlFile) {
        // TODO
        /*
        XMLContextReader xmlcr = new XMLComplexContextReader(xmlFile, this);
        if (!xmlcr.isRunning())
        {
        try
        {
        data = xmlcr.read();
        } catch (IncompatibleContextualObjectException icoe) {
        return false;
        }
        }*/
        return true;
    }

    public synchronized String extractHierarchyAsString() {
        String hierarchy = "" + this.name + "->";
        for (Enumeration e = contexts.elements(); e.hasMoreElements();) {
            hierarchy += ((Context) e.nextElement()).extractHierarchyAsString();
        }
        return hierarchy;
    }
}